package rsj.api.org.importdata;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.Cell;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import rsj.api.org.entity.ImportFailedReason;
import rsj.api.org.entity.ImportTestDataEntity;
import rsj.db.dbutils.UniqueID;
import rsj.util.HumpToLineUtil;
import common.util.validator.ValidatorFactoryUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 导入测试数据解析回调
 *
 * @author yuanqinglong
 * @since 2021/3/4 16:15
 */
public class ImportEasyExcelInvoke extends AnalysisEventListener<ImportTestDataEntity> {
    /**
     * 将excel中是否扣费的汉字（是、否）翻译对应code
     */
    public static final Map<String, String> COST_CHINESE_TO_CODE = new HashMap<>();
    public static final Map<String, String> COST_CODE_TO_CHINESE = new HashMap<>();

    static {
        ImportEasyExcelInvoke.COST_CHINESE_TO_CODE.put("是", ImportEasyExcelInvoke.IS_COST);
        ImportEasyExcelInvoke.COST_CHINESE_TO_CODE.put("否", ImportEasyExcelInvoke.NOT_COST);
        ImportEasyExcelInvoke.COST_CODE_TO_CHINESE.put("1", "是");
        ImportEasyExcelInvoke.COST_CODE_TO_CHINESE.put("0", "否");
    }

    private static final Logger logger = LoggerFactory.getLogger(ImportEasyExcelInvoke.class);


    /**
     * 数据导入时间（如果导入报表、通过这个时间查询本次导入失败详情）
     */
    private final LocalDateTime importTime;


    /**
     * 读取成功的数据
     */
    private final List<ImportTestDataEntity> successData;
    /**
     * 导入失败日志
     */
    private final List<ImportFailedReason> failureData;

    public ImportEasyExcelInvoke(LocalDateTime importTime, List<ImportTestDataEntity> successData, List<ImportFailedReason> failureData) {
        this.importTime = importTime;
        this.failureData = failureData;
        this.successData = successData;
    }

    /**
     * NOT_IGNORE：不忽略、 NOT_COST：不扣费
     */
    public static final String NOT_IGNORE = "00", NOT_COST = "0";
    /**
     * IGNORE：忽略、IS_COST：是扣费
     */
    public static final String IGNORE = "01", IS_COST = "1";


    /**
     * 每解析一行回调此方法
     *
     * @param importTestDataEntity 行数据
     * @param analysisContext      .
     */
    @Override
    public void invoke(ImportTestDataEntity importTestDataEntity, AnalysisContext analysisContext) {
        // 必填参数校验
        String reasonForFailure = ValidatorFactoryUtils.validator(importTestDataEntity);
        importTestDataEntity.setLogStoragetime(importTime);
        importTestDataEntity.setLogIscost(COST_CHINESE_TO_CODE.getOrDefault(importTestDataEntity.getLogIscost(), importTestDataEntity.getLogIscost()));
        importTestDataEntity.setLogDscost(COST_CHINESE_TO_CODE.getOrDefault(importTestDataEntity.getLogDscost(), importTestDataEntity.getLogDscost()));
        if (Objects.nonNull(reasonForFailure)) {
            this.addFailureData(importTestDataEntity, reasonForFailure, analysisContext);
            return;
        }
        this.addSuccessData(importTestDataEntity);
    }


    /**
     * 添加解析校验未通过的数据
     *
     * @param importTestDataEntity 解析的数据
     * @param reasonForFailure     错误原因
     */
    private void addFailureData(ImportTestDataEntity importTestDataEntity, String reasonForFailure, AnalysisContext analysisContext) {
        String reason = String.format("第%s行%s，请核实", analysisContext.readRowHolder().getRowIndex() + 1, reasonForFailure);
        JSONObject dataJson = JSONObject.parseObject(JSONObject.toJSONString(importTestDataEntity, SerializerFeature.WriteMapNullValue));
        JSONObject saveErrorData = new JSONObject();
        dataJson.forEach((key, value) -> {
            if (Objects.equals(key, "logIscost") || Objects.equals(key, "logDscost")) {
                value = COST_CODE_TO_CHINESE.getOrDefault(value, (String) value);
            }
            saveErrorData.put(HumpToLineUtil.toFirstUppercase(key), value);
        });
        ImportFailedReason importFailedReason = new ImportFailedReason(String.valueOf(UniqueID.generateId()), saveErrorData.toJSONString(), reason, NOT_IGNORE, importTestDataEntity.getLogStoragetime(), importTestDataEntity.getLogReqsernumber());
        failureData.add(importFailedReason);
    }


    /**
     * 添加解析校验通过的数据
     *
     * @param importTestDataEntity 解析的数据
     */
    private void addSuccessData(ImportTestDataEntity importTestDataEntity) {
        successData.add(importTestDataEntity);
    }


    @Override
    public boolean hasNext(AnalysisContext context) {
        return !CollectionUtils.isEmpty(context.readRowHolder().getCellMap());
    }


    /**
     * 解析完全部后 回调
     *
     * @param analysisContext .
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        logger.info("ImportEasyExcelInvoke：测试数据导入解析完成");
    }

    /**
     * 出现异常回调
     *
     * @param exception 异常
     * @param context   .
     * @throws Exception 返回异常
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        logger.error("解析失败，继续解析下一行:{}", exception.getMessage());
        // ExcelDataConvertException:当数据转换异常的时候，会抛出该异常，此处可以得知第几行，第几列的数据
        if (exception instanceof ExcelDataConvertException) {
            Map<String, Object> originalData = this.restoreOriginalData(context);
            ExcelDataConvertException dataConvertException = (ExcelDataConvertException) exception;
            int columnIndex = ((ExcelDataConvertException) exception).getColumnIndex() + 1;
            int rowIndex = ((ExcelDataConvertException) exception).getRowIndex() + 1;
            String reason = "第" + rowIndex + "行，第" + columnIndex + "列" + "数据格式有误，数据为：" + dataConvertException.getCellData().toString();
            ImportFailedReason importFailedReason = new ImportFailedReason(String.valueOf(UniqueID.generateId()), JSONObject.toJSONString(originalData), reason, NOT_IGNORE, importTime, originalData.getOrDefault("Log_Reqsernumber", "").toString());
            failureData.add(importFailedReason);
            logger.error(reason);
        }
    }


    /**
     * 封装原始数据对应关系
     *
     * @param context context 解析数据
     * @return 抛出异常此行的原始数据
     */
    private Map<String, Object> restoreOriginalData(AnalysisContext context) {
        Map<String, Object> originalData = new HashMap<>(16);
        Map<Integer, Cell> cellMap = context.readRowHolder().getCellMap();
        Map<Integer, Head> headMap = context.currentReadHolder().excelReadHeadProperty().getHeadMap();
        headMap.forEach((integer, head) -> {
            String stringValue = ((CellData) cellMap.get(integer)).getStringValue();
            if (Objects.equals(head.getFieldName(), "logIscost") || Objects.equals(head.getFieldName(), "logDscost")) {
                stringValue = COST_CODE_TO_CHINESE.getOrDefault(stringValue, stringValue);
            }
            originalData.put(HumpToLineUtil.toFirstUppercase(head.getFieldName()), stringValue);
        });
        return originalData;
    }


}
